#pragma once

#include <zenopy/PythonClient.hpp>
#include <zenox/glm.hpp>
#include <functional>
#include <vector>
#include <string>

namespace zenopy {

std::vector<std::string> stagePrimList(PythonClient &cli, std::string const &stageFile);
std::string stageDefaultPrim(PythonClient &cli, std::string const &stageFile);
std::string stagePrimName(PythonClient &cli, std::string const &stageFile, std::string const &primPath);
std::string stagePrimPropValString(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &propName);
std::string stagePrimPropType(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &propName);
std::string stagePrimPropType(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &propName);
std::string stagePrimPropValString(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &propName);
std::vector<std::string> stagePrimPropValStrList(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &propName);
std::vector<std::string> stagePrimPropValStrList(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &propName);
namespace details {
std::vector<size_t> _impl_stagePrimPropValArray(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &propName, std::function<void *(size_t)> const &outDelegate, int assumeShapeDim, int glmComponents, int Sca0Vec1Mat2, int glmCols, int glmRows, size_t sizeofT0);
}
std::vector<std::string> stagePrimPropNames(PythonClient &cli, std::string const &stageFile, std::string const &primPath);
std::vector<std::string> stagePrimChildren(PythonClient &cli, std::string const &stageFile, std::string const &primPath);
std::string stagePrimChild(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &childRelName);
std::string stagePrimParent(PythonClient &cli, std::string const &stageFile, std::string const &primPath);
std::string stagePrimType(PythonClient &cli, std::string const &stageFile, std::string const &primPath); // return value can be "Xform" "Mesh" etc.
bool stagePrimIsFlag(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &flagType); // flagType can be "Active" "Group" "Hidden" etc.

template <class T>
static std::vector<size_t> stagePrimPropValArray(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &propName, std::vector<T> &out, int assumeShapeDim = 1) {
    out.clear();
    std::function<void *(size_t)> outDelegate = [&] (size_t n) {
        out.resize(n);
        return (void *)out.data();
    };
    using Trait = zenox::glm_traits<T>;
    return details::_impl_stagePrimPropValArray(
        cli, stageFile, primPath, propName, outDelegate, assumeShapeDim,
        Trait::components, Trait::is_vec ? 1 : (Trait::is_mat ? 2 : 0),
        Trait::columns, Trait::rows, sizeof(typename Trait::value_type));
}

template <class T>
static T stagePrimPropValScalar(PythonClient &cli, std::string const &stageFile, std::string const &primPath, std::string const &propName) {
    std::vector<T> out;
    std::function<void *(size_t)> outDelegate = [&] (size_t n) {
        out.resize(n);
        return (void *)out.data();
    };
    using Trait = zenox::glm_traits<T>;
    details::_impl_stagePrimPropValArray(
        cli, stageFile, primPath, propName, outDelegate, 0,
        Trait::components, Trait::is_vec ? 1 : (Trait::is_mat ? 2 : 0),
        Trait::columns, Trait::rows, sizeof(typename Trait::value_type));
    if (out.size() != 1) [[unlikely]] zenox::fatal_error("in {}@{}, out array size should be 1, got {}", primPath, propName, out.size());
    return out.front();
}

}
